module mycpu(
    input clock,
    input reset,

    input data_r_valid,
    input data_w_valid,
    input [63:0] data_r_data,
    input [31:0] inst_r_data,
    input inst_r_valid,

    output inst_r_ena,
    output [63:0] inst_r_addr,
    output [63:0] data_w_addr,
    output [63:0] data_w_data,
    output data_w_ena,
    output data_r_ena,
    output [63:0] data_r_addr,
    output [7:0] mem_mask,
    output flush
    
);
    wire [5:0] stall;
    wire [63:0] npc;
    wire [63:0] pc;
    wire [31:0] if_id_o_inst;
    wire [63:0] if_id_o_inst_addr;
    wire [63:0] if_id_pc;
    wire [63:0] if_id_badvaddr;
    wire [63:0] if_id_excode;
    wire if_id_except_ena;
    wire id_write_to_mem;
    wire id_write_to_regfile;
    wire [4:0] id_reg_addr;
    wire [16:0] id_ALUControl;
    wire id_pcsource;
    wire [63:0] b_j_pc;
    wire [63:0] id_sext_imm;
    wire id_aluimm;
    wire [63:0] id_data1;
    wire [63:0] id_data2;
    wire id_is_jump;
    wire [3:0] id_mem_control;
    wire id_mem_to_regfile;
    wire [11:0] id_csr;
    wire [63:0] id_csr_wdata;
    wire [2:0] id_csr_control;
    wire [63:0] id_badvaddr;
    wire [63:0] id_excode;
    wire id_except_ena;
    wire [63:0] id_o_excode;
    wire id_o_except_ena;
    wire id_o_ret;
    wire id_o_shift;
    wire [63:0] id_o_shamt_ext;
    wire id_inst_r_valid;
    wire exe_write_to_mem;
    wire exe_write_to_regfile;
    wire [4:0] exe_reg_addr;
    wire [16:0] exe_ALUControl;
    wire [63:0] exe_sext_imm;
    wire exe_aluimm;
    wire [63:0] exe_data1;
    wire [63:0] exe_data2;
    wire [63:0] exe_jump_pc;
    wire exe_is_jump;
    wire [3:0] exe_mem_control;
    wire exe_mem_to_regfile;
    wire [63:0] exe_data;
    wire [63:0] exe_pc;
    wire [31:0] exe_inst;
    wire [11:0] exe_csr;
    wire [63:0] exe_csr_wdata;
    wire [2:0] exe_csr_control;
    wire [63:0] exe_badvaddr;
    wire [63:0] exe_excode;
    wire exe_except_ena;
    wire exe_ret;
    wire [63:0] exe_o_csr_data_res;
    wire exe_o_write_ena;
    wire exe_inst_r_valid;
    wire exe_shift;
    wire [63:0] exe_shamt_ext;
    wire mem_write_to_mem;
    wire mem_write_to_regfile;
    wire [4:0] mem_reg_addr;
    wire [63:0] mem_data1;
    wire [63:0] mem_data2;
    wire [3:0] mem_mem_control;
    wire mem_mem_to_regfile;
    wire [63:0] mem_data_o;
    wire [63:0] mem_pc;
    wire [31:0] mem_inst;
    wire [63:0] mem_badvaddr;
    wire [63:0] mem_excode;
    wire mem_except_ena;
    wire mem_wb_except_ena;
    wire [63:0] mem_o_badvaddr;
    wire [63:0] mem_o_excode;
    wire mem_ret;
    wire mem_inst_r_valid;
    wire [63:0] wb_write_to_regfile;
    wire wb_mem_to_regfile;
    wire [4:0] wb_reg_addr;
    wire [63:0] wb_alu_data;
    wire [63:0] wb_mem_data;
    wire [63:0] wb_data_out;
    wire [63:0] wb_pc;
    wire [31:0] wb_inst;
    wire [63:0] wb_badvaddr;
    wire [63:0] wb_excode;
    wire wb_except_ena;
    wire wb_ret;
    wire wb_inst_r_valid;
    wire [63:0] rdata1;
    wire [63:0] rdata2;
    wire [63:0] csr_data;
    wire [63:0] csr_wdata;
    wire csr_write_ena;

    wire flush_if_id;
    wire [63:0] new_pc;
    wire ret;
    wire [63:0] mtvec_pc;
    wire [63:0] epc;
    wire stall_from_if;
    wire stall_from_mem;
    wire [63:0] time_wdata;
    wire mtimecmp_ena;
    wire mtime_ena;
    wire mem_mtime_int;
    wire mtime_data;
    wire mtimecmp_data;
    wire csr_mtime_int;
    wire keep;

    wire [7:0] wdest;
    assign wdest = {3'b000, {wb_reg_addr[4:0]}};
    wire [63:0] regs[0:31];
    wire [63:0] delay_pc;
    wire [31:0] delay_inst;
    wire delay_write_to_regfile;
    wire [4:0] delay_reg_addr;
    wire [63:0] delay_data_out;
    wire delay_inst_r_valid;

    wire [63:0] csr_mstatus;
    wire [63:0] csr_mtvec;
    wire [63:0] csr_mepc;
    wire [63:0] csr_mcause;
    wire [63:0] csr_mie;
    wire [63:0] csr_mip;
    wire [63:0] csr_mscratch;
    wire [63:0] csr_sstatus;
    wire [63:0] delay_mstatus;
    wire [63:0] delay_mtvec;
    wire [63:0] delay_mepc;
    wire [63:0] delay_mcause;
    wire [63:0] delay_mie;
    wire [63:0] delay_mip;

/////////////////////////////////////////////
// Difftest
reg cmt_wen;
reg [7:0] cmt_wdest;
reg [63:0] cmt_wdata;
reg [63:0] cmt_pc;
reg [31:0] cmt_inst;
reg cmt_valid;
reg trap;
reg [7:0] trap_code;
reg [63:0] cycleCnt;
reg [63:0] instrCnt;
reg [63:0] regs_diff [0 : 31];
reg [63:0] mstatus;
reg [63:0] mtvec;
reg [63:0] mepc;
reg [63:0] mcause;
reg [63:0] mie;
reg [63:0] mip;
reg [63:0] mscratch;
reg [63:0] sstatus;
reg skip;

wire inst_valid = delay_inst_r_valid;
wire [63:0] a0;

always @(posedge clock) begin
  if (reset) begin
    {cmt_wen, cmt_wdest, cmt_wdata, cmt_pc, cmt_inst, cmt_valid, trap, trap_code, cycleCnt, instrCnt, skip} <= 0;
  end
  else if (~trap) begin
    cmt_wen <= delay_write_to_regfile;
    cmt_wdest <= {3'd0, delay_reg_addr};
    cmt_wdata <= delay_data_out;
    cmt_pc <= {delay_pc[63:32], 1'b1, delay_pc[30:0]};
    cmt_inst <= delay_inst;
    cmt_valid <= inst_valid;

    mstatus <= csr_mstatus;
    mtvec <= csr_mtvec;
    mepc <= csr_mepc;
    mcause <= csr_mcause;
    mie <= csr_mie;
    mip <= csr_mip;
    mscratch <= csr_mscratch;
    sstatus <= csr_sstatus;

		regs_diff <= regs;

    trap <= delay_inst[6:0] == 7'h6b;
    trap_code <= regs[10][7:0];
    cycleCnt <= cycleCnt + 1;
    instrCnt <= instrCnt + inst_valid;
    skip <= delay_inst == 32'h0000007b || delay_inst == 32'h0007b483 || delay_inst == 32'h00f73023 ? 1'b1 : 1'b0;
    if(delay_inst == 32'h0000007b) begin
      $write("%c", a0);
    end
  end
end

DifftestInstrCommit DifftestInstrCommit(
  .clock              (clock),
  .coreid             (0),
  .index              (0),
  .valid              (cmt_valid),
  .pc                 (cmt_pc),
  .instr              (cmt_inst),
  .skip               (skip),
  .isRVC              (0),
  .scFailed           (0),
  .wen                (cmt_wen),
  .wdest              (cmt_wdest),
  .wdata              (cmt_wdata)
);

DifftestArchIntRegState DifftestArchIntRegState (
  .clock              (clock),
  .coreid             (0),
  .gpr_0              (regs_diff[0]),
  .gpr_1              (regs_diff[1]),
  .gpr_2              (regs_diff[2]),
  .gpr_3              (regs_diff[3]),
  .gpr_4              (regs_diff[4]),
  .gpr_5              (regs_diff[5]),
  .gpr_6              (regs_diff[6]),
  .gpr_7              (regs_diff[7]),
  .gpr_8              (regs_diff[8]),
  .gpr_9              (regs_diff[9]),
  .gpr_10             (regs_diff[10]),
  .gpr_11             (regs_diff[11]),
  .gpr_12             (regs_diff[12]),
  .gpr_13             (regs_diff[13]),
  .gpr_14             (regs_diff[14]),
  .gpr_15             (regs_diff[15]),
  .gpr_16             (regs_diff[16]),
  .gpr_17             (regs_diff[17]),
  .gpr_18             (regs_diff[18]),
  .gpr_19             (regs_diff[19]),
  .gpr_20             (regs_diff[20]),
  .gpr_21             (regs_diff[21]),
  .gpr_22             (regs_diff[22]),
  .gpr_23             (regs_diff[23]),
  .gpr_24             (regs_diff[24]),
  .gpr_25             (regs_diff[25]),
  .gpr_26             (regs_diff[26]),
  .gpr_27             (regs_diff[27]),
  .gpr_28             (regs_diff[28]),
  .gpr_29             (regs_diff[29]),
  .gpr_30             (regs_diff[30]),
  .gpr_31             (regs_diff[31])
);

DifftestTrapEvent DifftestTrapEvent(
  .clock              (clock),
  .coreid             (0),
  .valid              (trap),
  .code               (trap_code),
  .pc                 (cmt_pc),
  .cycleCnt           (cycleCnt),
  .instrCnt           (instrCnt)
);

DifftestCSRState DifftestCSRState(
  .clock              (clock),
  .coreid             (0),
  .priviledgeMode     (3),
  .mstatus            (mstatus),
  .sstatus            (sstatus),
  .mepc               (mepc),
  .sepc               (0),
  .mtval              (0),
  .stval              (0),
  .mtvec              (mtvec),
  .stvec              (0),
  .mcause             (mcause),
  .scause             (0),
  .satp               (0),
  .mip                (mip),
  .mie                (mie),
  .mscratch           (mscratch),
  .sscratch           (0),
  .mideleg            (0),
  .medeleg            (0)
);

DifftestArchFpRegState DifftestArchFpRegState(
  .clock              (clock),
  .coreid             (0),
  .fpr_0              (0),
  .fpr_1              (0),
  .fpr_2              (0),
  .fpr_3              (0),
  .fpr_4              (0),
  .fpr_5              (0),
  .fpr_6              (0),
  .fpr_7              (0),
  .fpr_8              (0),
  .fpr_9              (0),
  .fpr_10             (0),
  .fpr_11             (0),
  .fpr_12             (0),
  .fpr_13             (0),
  .fpr_14             (0),
  .fpr_15             (0),
  .fpr_16             (0),
  .fpr_17             (0),
  .fpr_18             (0),
  .fpr_19             (0),
  .fpr_20             (0),
  .fpr_21             (0),
  .fpr_22             (0),
  .fpr_23             (0),
  .fpr_24             (0),
  .fpr_25             (0),
  .fpr_26             (0),
  .fpr_27             (0),
  .fpr_28             (0),
  .fpr_29             (0),
  .fpr_30             (0),
  .fpr_31             (0)
);

/////////////////////////////////////////////

    pc PC(.clk(clock), .reset(reset), .stall(stall), .b_j_pc(b_j_pc), .pcsource(id_pcsource), .new_pc(new_pc), .flush(flush), .ret(wb_ret), .flush_if_id(flush_if_id), .o_inst_addr(inst_r_addr), .o_badvaddr(if_id_badvaddr), .o_excode(if_id_excode), .o_except_ena(if_id_except_ena), .keep(keep));

    If IF(.clk(clock), .reset(reset), .inst_r_valid(inst_r_valid), .stall_from_mem(stall_from_mem), .flush_if_id(flush_if_id), .stall_o(stall_from_if), .inst_r_ena(inst_r_ena));

    regfile Regfile(.clk(clock), .reset(reset), .en(wb_write_to_regfile), .raddr1(if_id_o_inst[19:15]), .raddr2(if_id_o_inst[24:20]), .waddr(wb_reg_addr), .wdata(wb_data_out), .rdata1(rdata1), .rdata2(rdata2),
                    .regs_o(regs), .a0_data(a0));

    if_id IF_ID(.clk(clock), .reset(reset), .stall(stall), .i_inst(inst_r_data), .i_inst_addr(inst_r_addr), .i_pc(inst_r_addr), .i_badvaddr(id_badvaddr), .i_excode(id_excode), .i_except_ena(id_except_ena), .flush_if_id(flush_if_id), .flush(flush), .i_inst_r_valid(inst_r_valid), .keep(keep),
                .o_inst(if_id_o_inst), .o_inst_addr(if_id_o_inst_addr), .o_pc(if_id_pc),
                .o_badvaddr(id_badvaddr), .o_excode(id_excode), .o_except_ena(id_except_ena), .o_inst_r_valid(id_inst_r_valid));

    decode DECODE(.clk(clock), .reset(reset), .inst(if_id_o_inst), .inst_addr(if_id_o_inst_addr), .rdata1(rdata1), .rdata2(rdata2), .exe_out_addr(exe_reg_addr), .exe_out(exe_data), .mem_out_addr(mem_reg_addr),
                  .mem_out_1(mem_data1), .mem_out_2(mem_data_o), .exe_write_to_regfile(exe_write_to_regfile), .mem_write_to_regfile(mem_write_to_regfile), .exe_mem_to_regfile(exe_mem_to_regfile), .mem_mem_to_regfile(mem_mem_to_regfile),
                  .i_excode(id_excode), .i_except_ena(id_except_ena),
                  .write_to_mem(id_write_to_mem), .write_to_regfile(id_write_to_regfile), .reg_addr(id_reg_addr), .ALUControl(id_ALUControl), .b_j_pc(b_j_pc), .pcsource(id_pcsource),
                  .sext_imm(id_sext_imm), .aluimm(id_aluimm), .data1(id_data1), .data2(id_data2), .is_jump(id_is_jump), .mem_control(id_mem_control), .mem_to_regfile(id_mem_to_regfile), .csr(id_csr), .csr_wdata(id_csr_wdata),
                  .csr_control(id_csr_control), .o_excode(id_o_excode), .o_except_ena(id_o_except_ena), .flush_if_id(flush_if_id), .ret(id_o_ret), .shift(id_o_shift), .shamt_ext(id_o_shamt_ext));
    
    id_exe ID_EXE(.clk(clock), .reset(reset), .stall(stall), .i_write_to_mem(id_write_to_mem), .i_write_to_regfile(id_write_to_regfile), .i_reg_addr(id_reg_addr), .i_ALUControl(id_ALUControl),
                  .i_sext_imm(id_sext_imm), .i_aluimm(id_aluimm), .i_data1(id_data1), .i_data2(id_data2), .i_is_jump(id_is_jump), .i_mem_control(id_mem_control), .i_mem_to_regfile(id_mem_to_regfile),
                  .i_pc(if_id_pc), .i_inst(if_id_o_inst), .i_csr(id_csr), .i_csr_wdata(id_csr_wdata), .i_csr_control(id_csr_control), .i_badvaddr(id_badvaddr), .i_excode(id_o_excode), .i_except_ena(id_o_except_ena),
                  .flush(flush), .i_ret(id_o_ret), .i_inst_r_valid(id_inst_r_valid), .i_shift(id_o_shift), .i_shamt_ext(id_o_shamt_ext),
                  .o_write_to_mem(exe_write_to_mem), .o_write_to_regfile(exe_write_to_regfile), .o_reg_addr(exe_reg_addr), .o_ALUControl(exe_ALUControl), .o_sext_imm(exe_sext_imm),
                  .o_aluimm(exe_aluimm), .o_data1(exe_data1), .o_data2(exe_data2), .o_is_jump(exe_is_jump), .o_mem_control(exe_mem_control), .o_mem_to_regfile(exe_mem_to_regfile),
                  .o_pc(exe_pc), .o_inst(exe_inst), .o_csr(exe_csr), .o_csr_wdata(exe_csr_wdata), .o_csr_control(exe_csr_control), .o_badvaddr(exe_badvaddr), .o_excode(exe_excode), .o_except_ena(exe_except_ena), .o_ret(exe_ret), .o_inst_r_valid(exe_inst_r_valid),
                  .o_shift(exe_shift), .o_shamt_ext(exe_shamt_ext));
    
    exe EXE(.sext_imm(exe_sext_imm), .aluimm(exe_aluimm), .data1(exe_data1), .data2(exe_data2), .is_jump(exe_is_jump), .ALUControl(exe_ALUControl), .csr_control(exe_csr_control),
            .csr_wdata(exe_csr_wdata), .csr_data(csr_data), .i_pc(exe_pc), .shift(exe_shift), .shamt_ext(exe_shamt_ext), .data(exe_data), .csr_data_res(exe_o_csr_data_res), .write_ena(exe_o_write_ena));
    
    exe_mem EXE_MEM(.clk(clock), .reset(reset), .stall(stall), .i_write_to_mem(exe_write_to_mem), .i_write_to_regfile(exe_write_to_regfile), .i_reg_addr(exe_reg_addr), .i_data1(exe_data), .i_data2(exe_data2),
                    .i_mem_control(exe_mem_control), .i_mem_to_regfile(exe_mem_to_regfile), .i_pc(exe_pc), .i_inst(exe_inst),
                    .i_badvaddr(exe_badvaddr), .i_excode(exe_excode), .i_except_ena(exe_except_ena), .flush(flush), .i_ret(exe_ret), .i_inst_r_valid(exe_inst_r_valid),
                    .o_write_to_mem(mem_write_to_mem), .o_write_to_regfile(mem_write_to_regfile), .o_reg_addr(mem_reg_addr), .o_data1(mem_data1), .o_data2(mem_data2),
                    .o_mem_control(mem_mem_control), .o_mem_to_regfile(mem_mem_to_regfile), .o_pc(mem_pc), .o_inst(mem_inst),
                    .o_badvaddr(mem_badvaddr), .o_excode(mem_excode), .o_except_ena(mem_except_ena), .o_ret(mem_ret), .o_inst_r_valid(mem_inst_r_valid));
    
    CLINT clint(.clk(clock), .reset(reset), .wdata(time_wdata), .mtimecmp_ena(mtimecmp_ena), .mtime_ena(mtime_ena), .mtime_int(mem_mtime_int), .mtime_data(mtime_data), .mtimecmp_data(mtimecmp_data));

    mem MEM(.mem_data_i(data_r_data), .mem_addr(mem_data1), .mem_control(mem_mem_control), .i_badvaddr(mem_badvaddr), .i_excode(mem_excode), .data_w_valid(data_w_valid), .data_r_valid(data_r_valid), .mem_wdata(mem_data2),
            .i_except_ena(mem_except_ena), .mem_data_o(mem_data_o), .mem_mask(mem_mask), .data_r_ena(data_r_ena), .data_w_ena(data_w_ena), .data_w_addr(data_w_addr), .o_badvaddr(mem_o_badvaddr), .o_excode(mem_o_excode), .o_except_ena(mem_wb_except_ena), .stall_o(stall_from_mem),
            .data_w_data(data_w_data), .data_r_addr(data_r_addr), .mtime_ena(mtime_ena), .mtimecmp_ena(mtimecmp_ena), .time_wdata(time_wdata), .mtime_data(mtime_data), .mtimecmp_data(mtimecmp_data));

    mem_wb MEM_WB(.clk(clock), .reset(reset), .stall(stall), .i_write_to_regfile(mem_write_to_regfile), .i_mem_to_regfile(mem_mem_to_regfile), .i_reg_addr(mem_reg_addr), .i_alu_data(mem_data1), .i_mem_data(mem_data_o),
                  .i_inst(mem_inst), .i_pc(mem_pc), .i_badvaddr(mem_o_badvaddr), .i_excode(mem_o_excode), .i_except_ena(mem_wb_except_ena), .flush(flush), .i_ret(mem_ret), .i_inst_r_valid(mem_inst_r_valid), .i_mtime_int(mem_mtime_int),
                  .o_write_to_regfile(wb_write_to_regfile), .o_mem_to_regfile(wb_mem_to_regfile), .o_reg_addr(wb_reg_addr), .o_alu_data(wb_alu_data), 
                  .o_mem_data(wb_mem_data), .o_pc(wb_pc), .o_inst(wb_inst), .o_badvaddr(wb_badvaddr), .o_excode(wb_excode), .o_except_ena(wb_except_ena), .o_ret(wb_ret), .o_inst_r_valid(wb_inst_r_valid), .o_mtime_int(csr_mtime_int));
    
    wb WB(.mem_to_regfile(wb_mem_to_regfile), .alu_data(wb_alu_data), .mem_data(wb_mem_data), .data_out(wb_data_out));

    csr CSR(.clk(clock), .reset(reset), .csr_wdata(exe_o_csr_data_res), .i_csr(exe_csr), .write_ena(exe_o_write_ena), .except_ena(wb_except_ena), .i_addr(wb_pc), .i_badvaddr(wb_badvaddr), .i_excode(wb_excode), .badinst(wb_inst),
            .ret(wb_ret), .mtime_int(csr_mtime_int), .o_csr_data(csr_data), .mtvec_pc(mtvec_pc), .epc(epc), .mstatus_data(csr_mstatus), .mtvec_data(csr_mtvec), .mepc_data(csr_mepc), .mcause_data(csr_mcause), .mie_data(csr_mie), .mip_data(csr_mip), .mscratch_data(csr_mscratch),
            .sstatus_data(csr_sstatus));

    ctrl CTRL(.clk(clock), .reset(reset), .except_ena(wb_except_ena), .mtvec_pc(mtvec_pc), .epc(epc), .ret(wb_ret), .stall_from_if(stall_from_if), .stall_from_mem(stall_from_mem), .new_pc(new_pc), .flush(flush), .stall(stall), .inst_r_valid(inst_r_valid),
              .keep(keep));

    delay_stage DS(.clk(clock), .reset(reset), .i_pc(wb_pc), .i_inst(wb_inst), .i_write_to_regfile(wb_write_to_regfile), .i_reg_addr(wb_reg_addr), .i_data_out(wb_data_out), .i_inst_r_valid(wb_inst_r_valid), .i_mstatus(csr_mstatus),
                   .i_mtvec(csr_mtvec), .i_mepc(csr_mepc), .i_mcause(csr_mcause), .i_mie(csr_mie), .i_mip(csr_mip), .o_pc(delay_pc), .o_inst(delay_inst),
                   .o_write_to_regfile(delay_write_to_regfile), .o_reg_addr(delay_reg_addr), .o_data_out(delay_data_out), .o_inst_r_valid(delay_inst_r_valid), .o_mstatus(delay_mstatus), .o_mtvec(delay_mtvec), .o_mepc(delay_mepc),
                   .o_mcause(delay_mcause), .o_mie(delay_mie), .o_mip(delay_mip));
                   
endmodule